﻿using System;
using System.Collections.Generic;
using UnityEngine;
using XLua;

/// <summary>
/// 处理静态物件
/// </summary>
public class StaticSceneObjectController : MonoBehaviour
{
    /// <summary>
    /// 当前场景资源四叉树/八叉树
    /// </summary>
    private ISeparateTree<SceneObject> m_Tree;

    /// <summary>
    /// 刷新时间
    /// </summary>
    private float m_RefreshTime;

    private Vector3 m_OldRefreshPosition;
    private Vector3 m_OldRefreshRotation;

    /// <summary>
    /// 已加载的物体列表（频繁移除与添加使用双向链表）
    /// </summary>
    private LinkedList<SceneObject> m_LoadedObjectLinkedList;
    

    private TriggerHandle<SceneObject> m_TriggerHandle;
    
    private bool m_IsInitialized;
    
    private float m_MaxRefreshTime;

    private Action<SceneObject> m_OnEnter;

    private Action<SceneObject> m_OnExit;

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="center">场景区域中心</param>
    /// <param name="size">场景区域大小</param>
    /// <param name="maxRefreshTime">更新区域时间间隔</param>
    /// <param name="treeType"></param>
    /// <param name="quadTreeDepth">四叉树深度</param>
    /// <param name="onEnter"></param>
    /// <param name="onExit"></param>
    public void Init(Vector3 center, Vector3 size, float maxRefreshTime, TreeType treeType, int quadTreeDepth, Action<SceneObject> onEnter, Action<SceneObject> onExit)
    {
        if (m_IsInitialized)
            return;

        switch (treeType)
        {
            case TreeType.LinearOcTree:
                m_Tree = new LinearSceneOcTree<SceneObject>(center, size, quadTreeDepth);
                break;
            case TreeType.LinearQuadTree:
                m_Tree = new LinearSceneQuadTree<SceneObject>(center, size, quadTreeDepth);
                break;
            case TreeType.OcTree:
                m_Tree = new SceneTree<SceneObject>(center, size, quadTreeDepth, true);
                break;
            case TreeType.QuadTree:
                m_Tree = new SceneTree<SceneObject>(center, size, quadTreeDepth, false);
                break;
            default:
                m_Tree = new LinearSceneQuadTree<SceneObject>(center, size, quadTreeDepth);
                break;
        }

        m_LoadedObjectLinkedList = new LinkedList<SceneObject>();
        m_TriggerHandle = this.TriggerHandle;

        m_MaxRefreshTime = maxRefreshTime;

        m_IsInitialized = true;

        m_RefreshTime = maxRefreshTime;

        m_OnEnter = onEnter;
        m_OnExit = onExit;
    }


    void OnDestroy()
    {
        if (m_Tree != null)
            m_Tree.Clear();
        m_Tree = null;

        if (m_LoadedObjectLinkedList != null)
            m_LoadedObjectLinkedList.Clear();
        m_LoadedObjectLinkedList = null;
        m_TriggerHandle = null;
    }

    /// <summary>
    /// 添加场景物体
    /// </summary>
    /// <param name="obj"></param>
    public void AddSceneObject(ISceneObject obj)
    {
        if (!m_IsInitialized)
            return;
        if (m_Tree == null)
            return;
        if (obj == null)
            return;

        var sceneObject = new SceneObject(obj);
        sceneObject.Flag = SceneObject.CreateFlag.OutOfBounds;
        m_Tree.Add(sceneObject);
    }

    public void RefreshDetectorImmediately(IDetector detector)
    {
        if (!m_IsInitialized)
            return;

        detector.BoundsDirty = true;
        m_OldRefreshPosition = detector.Position;
        m_OldRefreshRotation = detector.Rotation;
        m_RefreshTime = 0;
        //进行触发检测
        m_Tree.Trigger(detector, m_TriggerHandle);
        //标记超出区域的物体
        MarkOutOfBoundsObjects();
    }

    /// <summary>
    /// 刷新触发器
    /// </summary>
    /// <param name="detector">触发器</param>
    public void RefreshDetector(IDetector detector)
    {
        if (!m_IsInitialized)
            return;

        //只有坐标发生改变才调用
        if (m_OldRefreshPosition != detector.Position || m_OldRefreshRotation != detector.Rotation)
        {
            m_RefreshTime += Time.deltaTime;
            //达到刷新时间才刷新，避免区域更新频繁
            if (m_RefreshTime > m_MaxRefreshTime)
            {
                m_OldRefreshPosition = detector.Position;
                m_OldRefreshRotation = detector.Rotation;
                m_RefreshTime = 0;
                // 刷新Bounds
                detector.BoundsDirty = true;
                //进行触发检测
                m_Tree.Trigger(detector, m_TriggerHandle);
                //标记超出区域的物体
                MarkOutOfBoundsObjects();
            }
        }
    }

    /// <summary>
    /// 四叉树触发处理函数
    /// </summary>
    /// <param name="sceneObject">与当前包围盒发生触发的场景物体</param>
    void TriggerHandle(SceneObject sceneObject)
    {
        if (sceneObject == null)
            return;

        if (sceneObject.Flag == SceneObject.CreateFlag.Old)
        {
            sceneObject.Flag = SceneObject.CreateFlag.New;
        }
        else if (sceneObject.Flag == SceneObject.CreateFlag.OutOfBounds)
        {
            // 进入检测范围
            m_OnEnter?.Invoke(sceneObject);

            sceneObject.Flag = SceneObject.CreateFlag.New;
            m_LoadedObjectLinkedList.AddFirst(sceneObject);
        }
    }

    /// <summary>
    /// 标记离开视野的物体
    /// </summary>
    void MarkOutOfBoundsObjects()
    {
        if (m_LoadedObjectLinkedList == null)
            return;

        var node = m_LoadedObjectLinkedList.First;
        while (node != null)
        {
            var sceneObject = node.Value;
            if (sceneObject.Flag == SceneObject.CreateFlag.Old)
            {
                sceneObject.Flag = SceneObject.CreateFlag.OutOfBounds;

                // 离开检测范围
                m_OnExit?.Invoke(sceneObject);

                var next = node.Next;
                m_LoadedObjectLinkedList.Remove(node);
                node = next;
            }
            else
            {
                sceneObject.Flag = SceneObject.CreateFlag.Old;
                node = node.Next;
            }
        }
    }

#if UNITY_EDITOR
    [BlackList]
    public int debug_DrawMinDepth = 0;
    [BlackList]
    public int debug_DrawMaxDepth = 5;
    [BlackList]
    public bool debug_DrawObj = true;
    void OnDrawGizmosSelected()
    {
        Color mindcolor = new Color32(0, 66, 255, 255);
        Color maxdcolor = new Color32(133, 165, 255, 255);
        Color objcolor = new Color32(0, 210, 255, 255);
        Color hitcolor = new Color32(255, 216, 0, 255);
        if (m_Tree != null)
            m_Tree.DrawTree(mindcolor, maxdcolor, objcolor, hitcolor, debug_DrawMinDepth, debug_DrawMaxDepth, debug_DrawObj);
    }
#endif
}